/**
 * 角色服务层
 * 负责处理角色相关的业务逻辑
 * 包括角色的创建、更新、删除、查询以及角色菜单分配等功能
 */
import Role from "../models/role.model.js";
import Menu from "../models/menu.model.js";
import logger from "../utils/logger.js";

class RoleService {
	/**
	 * 创建角色
	 * @param {Object} roleData - 角色数据对象
	 * @returns {Promise<Object>} 创建的角色对象
	 * @throws {Error} 业务逻辑错误
	 */
	static async createRole(roleData) {
		try {
			// 验证角色名称是否已存在
			if (await Role.isNameExist(roleData.name)) {
				throw new Error('参数错误：角色名称已存在');
			}
			
			// 验证角色编码是否已存在
			if (await Role.isCodeExist(roleData.code)) {
				throw new Error("参数错误：角色编码已存在");
			}
			
			// 创建角色
			const role = await Role.create(roleData);
			logger.info(`创建角色成功: ${roleData.name}`);
			return role;
		} catch (error) {
			logger.error(`创建角色失败: ${error.message}`);
			throw error;
		}
	}

	/**
	 * 更新角色
	 * @param {number} id - 角色ID
	 * @param {Object} roleData - 要更新的角色数据
	 * @returns {Promise<Object>} 更新后的角色对象
	 * @throws {Error} 业务逻辑错误
	 */
	static async updateRole(id, roleData) {
		try {
			// 检查角色是否存在
			const existRole = await Role.findById(id);
			if (!existRole) {
				throw new Error("请求错误：角色不存在");
			}

			// 如果修改了名称，检查名称是否已存在
			if (roleData.name && roleData.name !== existRole.name) {
				if (await Role.isNameExist(roleData.name, id)) {
					throw new Error("参数错误：角色名称已存在");
				}
			}

			// 如果修改了编码，检查编码是否已存在
			if (roleData.code && roleData.code !== existRole.code) {
				if (await Role.isCodeExist(roleData.code, id)) {
					throw new Error("参数错误：角色编码已存在");
				}
			}

			// 更新角色
			const updatedRole = await Role.update(id, roleData);
			logger.info(`更新角色成功: ID ${id}`);
			return updatedRole;
		} catch (error) {
			logger.error(`更新角色失败: ${error.message}`);
			throw error;
		}
	}

	/**
	 * 删除角色
	 * @param {number} id - 角色ID
	 * @returns {Promise<boolean>} 删除成功返回true
	 * @throws {Error} 业务逻辑错误
	 */
	static async deleteRole(id) {
		try {
			// 检查角色是否存在
			const role = await Role.findById(id);
			if (!role) {
				throw new Error("请求错误：角色不存在");
			}

			// 删除角色
			await Role.delete(id);
			logger.info(`删除角色成功: ID ${id}`);
			return true;
		} catch (error) {
			logger.error(`删除角色失败: ${error.message}`);
			throw error;
		}
	}

	/**
	 * 获取角色列表
	 * @param {Object} options - 查询选项
	 * @param {number} [options.page=1] - 页码
	 * @param {number} [options.limit=10] - 每页数量
	 * @param {string} [options.name] - 按名称筛选
	 * @param {string} [options.code] - 按编码筛选
	 * @param {number} [options.status] - 按状态筛选
	 * @returns {Promise<Object>} 包含角色列表和分页信息的对象
	 * @throws {Error} 业务逻辑错误
	 */
	static async getRoleList(options = {}) {
		try {
			// 获取角色列表
			const result = await Role.findAll(options);
			logger.info('获取角色列表成功');
			return result;
		} catch (error) {
			logger.error(`获取角色列表失败: ${error.message}`);
			throw error;
		}
	}

	/**
	 * 获取角色详情
	 * @param {number} id - 角色ID
	 * @returns {Promise<Object>} 角色详情对象
	 * @throws {Error} 业务逻辑错误
	 */
	static async getRoleDetail(id) {
		try {
			// 获取角色信息
			const role = await Role.findById(id);
			if (!role) {
				throw new Error("请求错误：角色不存在");
			}

			// 获取角色关联的菜单ID列表
			const menuIds = await Role.getRoleMenuIds(id);

			// 返回角色详情，包含菜单ID列表
			const roleDetail = {
				...role,
				menuIds
			};

			logger.info(`获取角色详情成功: ID ${id}`);
			return roleDetail;
		} catch (error) {
			logger.error(`获取角色详情失败: ${error.message}`);
			throw error;
		}
	}

	/**
	 * 为角色分配菜单
	 * @param {number} roleId - 角色ID
	 * @param {Array<number>} menuIds - 菜单ID数组
	 * @returns {Promise<boolean>} 分配成功返回true
	 * @throws {Error} 业务逻辑错误
	 */
	static async assignRoleMenus(roleId, menuIds) {
		try {
			// 检查角色是否存在
			const role = await Role.findById(roleId);
			if (!role) {
				throw new Error("请求错误：角色不存在");
			}

			// 如果提供了菜单ID，验证所有菜单是否存在
			if (menuIds && menuIds.length > 0) {
				// 使用Menu模型的validateMenuIds方法验证菜单ID是否存在
				const nonExistingMenuIds = await Menu.validateMenuIds(menuIds);
				
				// 如果存在无效的菜单ID，抛出错误
				if (nonExistingMenuIds.length > 0) {
					throw new Error(`参数错误：以下菜单ID不存在: ${nonExistingMenuIds.join(', ')}`);
				}
			}

			// 为角色分配菜单
			await Role.assignMenus(roleId, menuIds);
			logger.info(`角色菜单分配成功: 角色ID ${roleId}`);
			return true;
		} catch (error) {
			logger.error(`角色菜单分配失败: ${error.message}`);
			throw error;
		}
	}

	/**
	 * 通过ID查找角色
	 * @param {number} id - 角色ID
	 * @returns {Promise<Object|null>} 角色对象，如果不存在返回null
	 * @throws {Error} 数据库操作错误
	 */
	static async findById(id) {
		try {
			return await Role.findById(id);
		} catch (error) {
			logger.error(`查找角色失败: ${error.message}`);
			throw error;
		}
	}

	/**
	 * 通过编码查找角色
	 * @param {string} code - 角色编码
	 * @returns {Promise<Object|null>} 角色对象，如果不存在返回null
	 * @throws {Error} 数据库操作错误
	 */
	static async findByCode(code) {
		try {
			return await Role.findByCode(code);
		} catch (error) {
			logger.error(`通过编码查找角色失败: ${error.message}`);
			throw error;
		}
	}

	/**
	 * 根据角色ID获取菜单树型结构数据
	 * @param {number} roleId - 角色ID
	 * @returns {Promise<Array>} 菜单树型结构数组
	 * @throws {Error} 业务逻辑错误
	 */
	static async getRoleMenuTree(roleId) {
		try {
			// 检查角色是否存在
			const role = await Role.findById(roleId);
			if (!role) {
				throw new Error('角色不存在');
			}

			// 获取角色关联的菜单列表
			const menus = await Menu.findMenusByRoleId(roleId);
			
			// 构建树型结构
			const menuMap = {};
			const menuTree = [];

			// 第一步：将所有菜单放入映射表中，以ID为键
			menus.forEach(menu => {
				// 初始化children数组
				menu.children = [];
				menuMap[menu.id] = menu;
			});

			// 第二步：构建树形结构
			menus.forEach(menu => {
				if (menu.parent_id === 0) {
					// 顶级菜单直接添加到结果数组
					menuTree.push(menu);
				} else {
					// 非顶级菜单添加到其父菜单的children数组
					const parent = menuMap[menu.parent_id];
					if (parent) {
						parent.children.push(menu);
					}
				}
			});

			// 第三步：处理空的children数组
			const removeEmptyChildren = menus => {
				menus.forEach(menu => {
					if (menu.children.length === 0) {
						delete menu.children;
					} else {
						removeEmptyChildren(menu.children);
					}
				});
			};

			removeEmptyChildren(menuTree);

			logger.info(`获取角色菜单树成功: 角色ID ${roleId}`);
			return menuTree;
		} catch (error) {
			logger.error(`获取角色菜单树失败: ${error.message}`);
			throw error;
		}
	}
}

export default RoleService;